package vn.edu.tdt.it.domain;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import de.tudarmstadt.ukp.wikipedia.api.Category;
import de.tudarmstadt.ukp.wikipedia.api.DatabaseConfiguration;
import de.tudarmstadt.ukp.wikipedia.api.Page;
import de.tudarmstadt.ukp.wikipedia.api.WikiConstants.Language;
import de.tudarmstadt.ukp.wikipedia.api.Wikipedia;

import vn.edu.tdt.it.common.OWLConst;
import vn.edu.tdt.it.common.VietnameseProcessing;
import vn.edu.tdt.it.entity.Entity;
import vn.edu.tdt.it.entity.MyProperty;

/**
 * <b>Luận văn tốt nghiêp</b>
 * 
 * <pre>
 * Xây dựng ontology dựa trên CSDL có quan hệ giữa các entity
 * </pre>
 * 
 * @since 2014/11/22
 * @author nbpla_000
 * 
 */
public class OWLBuilder implements Runnable {

	// Một vài static fields cần dùng
	public static XMLOutputFactory outputFactory;
	public static XMLEventWriter eventWriter;
	public static XMLEventFactory eventFactory;
	public static XMLEvent end;
	public static XMLEvent tab;
	public static int count;
	public static Hashtable<Integer, Integer> table;

	/**
	 * <b>Init biến</b>
	 * 
	 * <pre>
	 * Tạo các biến cần dùng cho việc ghi file
	 * </pre>
	 * 
	 * @since 2014/11/22
	 * @serialData S01
	 * @author nbpla_000
	 */
	public static void init() {
		// Init các biến cần sử dụng
		try {
			table = new Hashtable<Integer, Integer>();
			count = 0;
			outputFactory = XMLOutputFactory.newInstance();
			eventWriter = outputFactory
					.createXMLEventWriter(new FileOutputStream(
							OWLConst.FILE_NAME));
			eventFactory = XMLEventFactory.newInstance();
			// Viết tag start
			StartDocument startDocument = eventFactory.createStartDocument();
			eventWriter.add(startDocument);
			end = eventFactory.createDTD("\n");
			tab = eventFactory.createDTD("\t");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (XMLStreamException e) {
			e.printStackTrace();
		}
	}

	/**
	 * <b>Ghi một property</b>
	 * 
	 * <pre>
	 * Ghi 1 tag property vào file
	 * </pre>
	 * 
	 * @since 2014/11/22
	 * @serialData S02
	 * @author nbpla_000
	 */
	public static void createProperty(String type, String value, Object innerVal)
			throws Exception {
		createNode("owl:ObjectProperty", type, value, innerVal);
	}

	/**
	 * <b>Tạo tag</b>
	 * 
	 * <pre>
	 * Ghi 1 tag với 1 attribute
	 * </pre>
	 * 
	 * @since 2014/11/22
	 * @serialData S02
	 * @author nbpla_000
	 */
	private static void createNode(String name, String atrname, String atrval,
			Object val) throws XMLStreamException {
		StartElement sElement = eventFactory.createStartElement("", "", name);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(atrname, atrval));
		Characters characters = eventFactory.createCharacters(String
				.valueOf(val));
		eventWriter.add(characters);
		EndElement eElement = eventFactory.createEndElement("", "", name);
		eventWriter.add(eElement);
		eventWriter.add(end);
	}

	/**
	 * <b>Tạo tag</b>
	 * 
	 * <pre>
	 * Ghi 1 tag property
	 * </pre>
	 * 
	 * @since 2014/11/22
	 * @serialData S02
	 * @author nbpla_000
	 */
	public static void createNode(MyProperty p) throws XMLStreamException {
		StartElement sElement = eventFactory.createStartElement("", "",
				OWLConst.BTAG_OBJ_PROP);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_ID,
				p.getName()));

		if (p.getDomain() != null && p.getDomain() != "") {
			eventWriter.add(end);
			createNode(OWLConst.TAG_DOMAIN, p.getDomain());
		}

		/*if (p.getRange() != null && p.getRange() != "") {
			eventWriter.add(end);
			createNode(OWLConst.TAG_RANGE, p.getRange());
		}*/
		eventWriter.add(tab);
		EndElement eElement = eventFactory.createEndElement("", "",
				OWLConst.BTAG_OBJ_PROP);
		eventWriter.add(eElement);
		eventWriter.add(end);

	}

	/**
	 * <b>Tạo tag</b>
	 * 
	 * <pre>
	 * Ghi 1 tag simple
	 * </pre>
	 * 
	 * @since 2014/11/22
	 * @serialData S02
	 * @author nbpla_000
	 */
	private static void createNode(String name, String value)
			throws XMLStreamException {

		StartElement sElement = eventFactory.createStartElement("", "", name);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_RESOURCE,
				value));
		// create End node
		EndElement eElement = eventFactory.createEndElement("", "", name);
		eventWriter.add(eElement);
		eventWriter.add(end);

	}

	/**
	 * <b>Tạo tag</b>
	 * 
	 * <pre>
	 * Ghi 1 tag con id
	 * </pre>
	 * 
	 * @since 2014/11/22
	 * @serialData S02
	 * @author nbpla_000
	 */
	public static void createSmallTag(String name, String value, String id)
			throws XMLStreamException {

		StartElement sElement = eventFactory.createStartElement("", "", name);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_ID, id));
		Characters chr = eventFactory.createCharacters(value);
		eventWriter.add(chr);
		EndElement eElement = eventFactory.createEndElement("", "", name);
		eventWriter.add(eElement);
		eventWriter.add(end);

	}

	public static void createNode(Entity entity) throws XMLStreamException {
		count++;
		StartElement sElement = eventFactory.createStartElement("", "",
				OWLConst.BTAG_CLASS);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_ID,
				entity.getName()));
		createSubclassTag(VietnameseProcessing.removeSpecialChar(entity
				.getSubClass()));
		if (entity.getPropName().size() != 0) {
			int index = 0;
			for (String item : entity.getPropName()) {
				StringBuilder sb = new StringBuilder();
				sb.append("Thing:");
				sb.append(item);
				createSmallTag(
						sb.toString(),
						entity.getTxtName().get(index),
						VietnameseProcessing.removeSpecialChar(sb.append("_")
								.append(entity.getName()).append("_")
								.append(entity.getPropCD().get(index))
								.toString()));
				index++;
			}
		}

		EndElement eElement = eventFactory.createEndElement("", "",
				OWLConst.BTAG_CLASS);
		eventWriter.add(eElement);
		eventWriter.add(end);

	}

	public static void createSubclassTag(String id) throws XMLStreamException {
		StartElement sElement = eventFactory.createStartElement("", "",
				OWLConst.TAG_SUBCLASSOF);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_RESOURCE,
				"#" + id));
		EndElement eElement = eventFactory.createEndElement("", "",
				OWLConst.TAG_SUBCLASSOF);
		eventWriter.add(eElement);
		eventWriter.add(end);
	}

	public static void createDictionaryNode(String name, String parent)
			throws XMLStreamException {
		count++;
		StartElement sElement = eventFactory.createStartElement("", "",
				OWLConst.BTAG_CLASS);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_ID,
				VietnameseProcessing.removeSpecialChar(name)));
		if (parent != null && !"".equals(parent)) {
			createEquivalentTag(VietnameseProcessing.removeSpecialChar(parent));
		}
		EndElement eElement = eventFactory.createEndElement("", "",
				OWLConst.BTAG_CLASS);
		eventWriter.add(eElement);
		eventWriter.add(end);

	}

	public static void createEquivalentTag(String id) throws XMLStreamException {
		StartElement sElement = eventFactory.createStartElement("", "",
				OWLConst.TAG_EQUIVALENTCLASS);
		eventWriter.add(tab);
		eventWriter.add(sElement);
		eventWriter.add(eventFactory.createAttribute(OWLConst.TYPE_RESOURCE,
				"#" + id));
		EndElement eElement = eventFactory.createEndElement("", "",
				OWLConst.TAG_EQUIVALENTCLASS);
		eventWriter.add(eElement);
		eventWriter.add(end);
	}

	public void run() {
		try {
			System.err.println("Running: " + Thread.currentThread().getName());
			Category khoahoctunhien = wiki.getCategory(Thread.currentThread()
					.getName());
			table.put(khoahoctunhien.getPageId(), 0);
			processCategory(mpProp, khoahoctunhien, "Thing");
			/*
			 * EndElement configEndElement = eventFactory.createEndElement("",
			 * "", "rdf:RDF"); eventWriter.add(configEndElement);
			 */
			eventWriter.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	static Map<String, Object> mpProp;
	static Wikipedia wiki;

	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws Exception {
		DatabaseConfiguration dbConfig = new DatabaseConfiguration("localhost",
				"wiki", "root", "", Language.vietnamese);
		wiki = new Wikipedia(dbConfig);
		init();
		/*
		 * StartElement configStartElement = eventFactory.createStartElement("",
		 * "", OWLConst.TAG_BEGIN); eventWriter.add(end);
		 * eventWriter.add(configStartElement); eventWriter.add(end);
		 */
		Entity en = new Entity("Thing", "Thing", "");
		createNode(en);
		File propFile = new File("input/properties.txt");
		mpProp = PropertyMapBuilder.buildProp(propFile);

		for (String item : (List<String>) mpProp.get(OWLConst.LST_PROPERTY)) {
			System.out.println("[INFO] Created Prop:" + item);
			MyProperty p = new MyProperty(item, "#Thing","");
			createNode(p);
		}
		Thread t1 = new Thread(new OWLBuilder());
		t1.setName("Khoa học tự nhiên");
		// Thread t2 = new Thread(new OWLBuilder());
		// t2.setName("Kỹ thuật");
		t1.start();
		// t2.start();
	}

	private static void processCategory(Map<String, Object> mpProp,
			Category category, String parentCat) throws Exception {
		Set<Page> pages = category.getArticles();
		boolean isExistPage = false;
		for (Page p : pages) {
			if (VietnameseProcessing.removeSpecialChar(p.getTitle().toString())
					.equals(VietnameseProcessing.removeSpecialChar(category
							.getTitle().toString()))) {
				processPages(mpProp, p, parentCat);
			}
			isExistPage = true;
			break;
		}
		if (!isExistPage) {
			Entity entity = new Entity(
					VietnameseProcessing.removeSpecialChar(category.getTitle()
							.toString()), category.getTitle().toString(),
					parentCat);
			createNode(entity);
		}
		for (Page p : pages) {
			if (!VietnameseProcessing.removeSpecialChar(p.getTitle().toString())
					.equals(VietnameseProcessing.removeSpecialChar(category
							.getTitle().toString()))) {
				System.out.println("[INFO] Processing page: " + p.getTitle()
						+ "/" + category.getTitle());
				processPages(mpProp, p, category.getTitle().toString());
			}
		}
		Set<Category> catSet = category.getChildren();
		for (Category cat : catSet) {
			if(cat!=null && !table.containsKey(cat.getPageId())){
				table.put(cat.getPageId(), 0);
				processCategory(mpProp, cat, category.getTitle().toString());
			}
		}

	}

	@SuppressWarnings("unchecked")
	private static void processPages(Map<String, Object> mpProp, Page page,
			String category) throws Exception {
		synchronized (StaticConnection.getConnection()) {
			System.err.println("[INFO] Processed: " + count);

			Map<String, String> mpRet = (Map<String, String>) mpProp
					.get(OWLConst.MP_PROPERTY);
			String sql = "SELECT id, title, type FROM entity WHERE title = \""
					+ page.getTitle().toString() + "\"";
			ResultSet rs = StaticConnection.getStatement().executeQuery(sql);
			if (rs != null)
				if (rs.next()) {
					sql = "SELECT pid, txt, type FROM property WHERE id = ?";
					PreparedStatement p = StaticConnection
							.getPreparedStatement(sql);
					p.setString(1, rs.getString(1));
					System.out.println("Processing " + rs.getString(1));
					ResultSet newRs = p.executeQuery();
					String name = VietnameseProcessing.removeSpecialChar(rs
							.getString(2));
					String title = rs.getString(2);
					Entity entity = new Entity(name, title, category);
					while (newRs.next()) {
						String temp = newRs.getString(1);
						System.out.println(">>>>Processing "
								+ newRs.getString(1));
						String cd = newRs.getString(2);
						String txt = "";
						String type = newRs.getString(3);
						if (temp != null && mpRet.containsKey(temp)) {
							boolean check = true;
							if (OWLConst.IND_OBJECT.equals(type)) {
								sql = "SELECT title FROM entity WHERE id = ?";
								PreparedStatement pre = StaticConnection
										.getPreparedStatement(sql);
								pre.setString(1, cd);
								ResultSet rset = pre.executeQuery();
								if (rset.next()) {
									txt = rset.getString(1);
								} else {
									check = false;
								}
							} else {
								txt = cd;
							}
							if (check) {
								entity.addProp(mpRet.get(temp));
								entity.addTxt(txt);
								entity.addPropCD(cd);
							}
						}
					}
					createNode(entity);
				}
		}
		// Equivalent Class
		String sqll = "SELECT id, name, pageId FROM pagemapline WHERE pageId = ? ORDER BY id ASC";
		PreparedStatement ps = StaticConnection.getPreparedStatement(sqll);
		ps.setInt(1, page.getPageId());
		ResultSet rs2 = ps.executeQuery();
		String pname = page.getTitle().toString();
		List<String> child = new ArrayList<String>();
		while (rs2.next()) {
			int id = rs2.getInt(1);
			String name = rs2.getString(2);
			int pid = rs2.getInt(3);
			if (id != pid) {
				child.add(name);
			}
		}
		for (String n : child) {
			System.out.println("Processing child: " + n);
			OWLBuilder.createDictionaryNode(n, pname);
		}
	}
}
